package com.fastloan.core.apiauth.aop;

import com.alibaba.fastjson.JSON;
import com.fastloan.common.model.CheckResult;
import com.fastloan.core.apiauth.anno.DecryptParam;
import com.fastloan.core.apiauth.base.service.impl.ry.model.RYRequestParam;
import com.fastloan.core.apiauth.service.EncryptUtil;
import com.kawins.result.ResultEnum;
import com.kawins.result.ResultTo;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;

public class EncryptArgumentResolver implements HandlerMethodArgumentResolver {

    private static final Logger logger = LoggerFactory.getLogger(EncryptArgumentResolver.class);

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(DecryptParam.class) ;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        HttpInputMessage httpInputMessage = new ServletServerHttpRequest(servletRequest);
        InputStream body = httpInputMessage.getBody();
        String json = IOUtils.toString(body, "UTF-8");
        logger.debug("原始请求解密信息:"+json);
        RYRequestParam ryRequestParam;
        try {
            ryRequestParam = JSON.parseObject(json, RYRequestParam.class);
        } catch (Exception e) {
            returnError(JSON.toJSONString(new ResultTo(2000, "数据非法!")),webRequest);
            logger.debug("数据非法:"+json);
            e.printStackTrace();
            return null;
        }
        servletRequest.setAttribute("ryRequestParam",ryRequestParam);
        CheckResult checkResult = EncryptUtil.ryVerifyAndDecrypt(ryRequestParam);
        if (!checkResult.isCheckResult()){
            String errorMsg = JSON.toJSONString(new ResultTo(checkResult.getCode(), checkResult.getCheckMsg()));
            returnError(errorMsg,webRequest);
            logger.debug("解密失败!原因："+checkResult.getCheckMsg());
            return null;
        }
        Object attribute = JSON.parseObject(String.valueOf(checkResult.getData()),parameter.getParameterType());
        DecryptParam decryptParam = parameter.getParameterAnnotation(DecryptParam.class);
        if(decryptParam.valid()){
            return validBean(parameter, webRequest, binderFactory, attribute);
        } else {
            return attribute;
        }
    }

    private Object validBean(MethodParameter parameter,NativeWebRequest webRequest
            , WebDataBinderFactory binderFactory, Object object) throws Exception {
        if (binderFactory != null) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, object, parameter.getParameterName());
            if (binder.getTarget() != null) {
                // 进行参数绑定，此方法实现不再赘述，可到上节查看
                bindRequestParameters(binder, parameter,object);
                // 如果有校验报错，会将结果放在binder.bindingResult属性中
                binder.validate();
                // 如果参数中不包含BindingResult参数，直接返回错误
                BindingResult bindingResult = binder.getBindingResult();
                if (bindingResult.hasErrors()) {
                    String errorMsg = bindingResult.getFieldErrors().stream()
                            .map(error -> "字段[" + error.getField() + "]错误:" + error.getDefaultMessage())
                            .collect(Collectors.joining(";"));
                    logger.debug("参数错误:"+errorMsg);
                    returnError(JSON.toJSONString(new ResultTo(ResultEnum.PARAM_ERROR, errorMsg)),webRequest);
                    return null;
                }
            }
        }
        return object;
    }

    private void returnError(String errorMsg,NativeWebRequest webRequest) throws IOException {
        HttpServletResponse servletResponse = webRequest.getNativeResponse(HttpServletResponse.class);
        servletResponse.setCharacterEncoding("UTF-8");
        servletResponse.setContentType("application/json; charset=utf-8");
        servletResponse.getWriter().append(errorMsg);
    }

    private void bindRequestParameters(WebDataBinder binder, MethodParameter parameter,Object object) {
        PropertyValues propertyValues = new MutablePropertyValues(Collections
                .singletonList(new PropertyValue(parameter.getParameterName(), object)));
        binder.bind(propertyValues);
    }
}